<?php
namespace App\DesignPattern\Adapter;

use App\DesignPattern\Adapter\DbAdapter;

class DbMysqli implements DbAdapter
{

    private $mysqli = null;

    public function __construct($host, $user, $pwd, $dbname, $port = null, $charset = "utf8", array $opts = null)
    {
        $real = isset($opts['real']) ? $opts['real'] : true;
        $socket = isset($opts['socket']) ? $opts['socket'] : true;
        $ssl_set = isset($opts['ssl_set']) ? $opts['ssl_set'] : [];
        $flags = isset($opts['flags']) ? $opts['flags'] : null;
        $opts = isset($opts['opts']) ? $opts['opts'] : [];
        if($real){
            $this->mysqli = new \mysqli();
            $this->mysqli->init();
            foreach ($opts as $key => $value) {
                $this->mysqli->options($key , $value);
            }
            if(isset($ssl_set['ENABLE']) && $ssl_set['ENABLE'] == true){
                $this->mysqli->ssl_set($ssl_set['KEY'], $ssl_set['CERT'], $ssl_set['CA'], $ssl_set['CAPATH'], $ssl_set['CIPHER']);
            }
            $this->mysqli->real_connect($host, $user, $pwd, $dbname, $port, $socket, $flags);
        }else{
            $this->mysqli = new \mysqli($host, $user, $pwd, $dbname, $port, $socket);
        }
        if(!$this->mysqli->connect_errno){
            $this->mysqli->set_charset($charset);
        }
    }

    public function __destruct()
    {
        $thread_id = $this->mysqli->thread_id ;
        $this->mysqli->kill($thread_id);
        $this->mysqli->close();
        $this->mysqli = null;
    }

    public function prototype()
    {
        return $this->mysqli;
    }

    public function query($sql, array $params = [], callable $callback = null)
    {
        $stmt = $this->mysqli->prepare($sql);
        if($stmt){
            if(!empty($params)){
                $all_params = [];
                $vtypes = ""; //数值类型
                foreach ($params as $val){
                    //类型判断
                    if(is_integer($val)){
                        $vtypes .= "i";
                    }elseif(is_double($val)){
                        $vtypes .= "d";
                    }elseif(is_object($val) || is_resource($val)){
                        $vtypes .= "b";
                    }else{
                        $vtypes .= "s";
                    }
                    $all_params[] = &$val;
                }
                array_unshift( $all_params , $vtypes ); //插入数值类型
                $fun_params = array();
                foreach (array_keys($all_params) as $k){
                    $fun_params[] = &$all_params[$k]; //注意此处的引用
                }
                call_user_func_array(array($stmt, "bind_param"), $fun_params); //由于bind_param方法的参数个数不确定，目前方法以call_user_func_array解决
            }
            $result = $stmt->execute();
            if($result){
                if(stripos($sql, "INSERT") === 0 || stripos($sql, "REPLACE") === 0){
                    $id = $stmt->insert_id;
                    $stmt->close();
                    return $id; //返回自增ID
                }elseif(stripos($sql, "SELECT") === 0){
                    $meta = $stmt->result_metadata();
                    $meta = $stmt->get_result();
                    $out = [];
                    if($callback !== null){
                        while ($assoc = $meta->fetch_assoc()){
                            $callback($assoc);
                        }
                        $meta->free();
                        $stmt->close();
                    }else{
                        while ($assoc = $meta->fetch_assoc()){
                            $out[] = $assoc;
                        }
                        $meta->free();
                        $stmt->close();
                        return $out; //返回数组
                    }
                }else{
                    $rows = $stmt->affected_rows;
                    $stmt->close();
                    return $rows; //返回受影响条数
                }
            }else{
                return false;
            }
        }else{
            return false;
        }
    }
}